package ch.zhaw.skuh.turing.model.turingmaschine.rules;

import ch.zhaw.skuh.turing.model.turingmaschine.Multiplication_N_ary;

public class NG_Multiplication extends RuleSet {

	protected int radix;

	public NG_Multiplication(Multiplication_N_ary tc) {
		super(tc);
		this.radix = tc.getRadix();
	}

	@Override
	public int setRules(int minState, int exitNormal, int exitSpecial,
			int[] tapes) {

		// *** multiplication rules ***

		int[] tapesCurrent;
		int currentState = minState;
		int marker = 0;

		tapesCurrent = new int[] { tapes[0], tapes[1], tapes[2], tapes[3] };

		// q0 to q(radix-1)
		// multiplication, exit to qX where X is overrun
		currentState = addRule_MultplyOverRunToQ(minState,
				minState + radix + 1, tapesCurrent, marker);

		// qX (x=radix)
		// prepare for multiplication, store trailing zeros to t4 and set marker
		currentState = addRule_HandleTrailingZeros(currentState, tapesCurrent,
				marker);

		// qX+1 (x=radix)
		// correct positions to marker
		currentState = addRule_CorrectPositionToM(currentState,
				currentState + 1, tapesCurrent, marker);

		// qX+2 (x=radix)
		// move trailing "0" from t4 to t2
		currentState = addRule_MoveTrailingZeros(currentState, exitNormal,
				tapesCurrent, marker);

		return currentState;
	}

	// fills qx to q(x+radix) !!
	protected int addRule_MultplyOverRunToQ(int qMin, int exitNormal,
			int[] tapes, int marker) {

		String m = Integer.toString(marker, radix);

		for (int q = 0; q < radix; q++) {
			for (int iT1 = 0; iT1 < radix; iT1++) {
				// iT2 to radix, meaning "_"
				for (int iT2 = 0; iT2 <= radix; iT2++) {
					// iT3 to radix, meaning "_"
					for (int iT3 = 0; iT3 <= radix; iT3++) {
						// iT4 either -1 or 0
						for (int iT4 = -1; iT4 < 1; iT4++) {

							String t1 = Integer.toString(iT1, radix);
							String t2 = (iT2 == radix) ? "_" : Integer
									.toString(iT2, radix);
							String t3 = (iT3 == radix) ? "_" : Integer
									.toString(iT3, radix);

							// 0 means marker, -1 means "_" !!
							String t4 = (iT4 == -1) ? "_" : m;

							String tOld = t1 + t2 + t3 + t4;
							int qOld = qMin + q;

							int qNext = qMin + getNextQ(iT1, iT2, iT3, q);
							String tNew = getMultipliedValues(iT1, iT2, iT3,
									t4, q);

							if (iT2 != radix) {
								// XXX_ normal multiply --> continue
								if (t4.compareTo(m) != 0) {
									tc.addRulePadded(qOld, tOld, qNext, tNew,
											"0-1-1-1", tapes);
								}
								// XX_0, t2>0, normal multiply --> continue
								else if (t4.compareTo(m) == 0 && iT2 > 0
										&& iT3 == radix) {
									tc.addRulePadded(qOld, tOld, qNext, tNew,
											"0-1-1-1", tapes);
								}
							} else
							// X___, qNew>0, eventually write new leading char
							// or avoid leading 0
							// --> exit q11
							if ((iT2 == radix || iT3 > 0) && iT3 == radix
									&& t4.compareTo("_") == 0) {
								tc.addRulePadded(qOld, tOld, exitNormal, tNew,
										"-1101", tapes);

							}
						}

					}
				}
			}

		}
		return qMin + radix;
	}

	// prepare for multiplication, store trailing zeros to t4 and set marker
	protected int addRule_HandleTrailingZeros(int q, int[] tapes, int marker) {

		String m = Integer.toString(marker, radix);

		for (int iT1 = 0; iT1 < radix; iT1++) {
			for (int iT2 = 0; iT2 < radix; iT2++) {
				String t1 = Integer.toString(iT1, radix);
				String t2 = Integer.toString(iT2, radix);
				// t3 and t4 = "_";
				String t3 = "_";
				String t4 = "_";

				String tOld = t1 + t2 + t3 + t4;

				// t1 = 0: copy to t4 --> continue
				if (iT1 == 0) {
					tc.addRulePadded(q, tOld, q, t1 + t2 + t3 + t1, "-100-1",
							tapes);
				} else

				// t2 = 0: move to t4 --> continue
				if (iT2 == 0) {
					tc.addRulePadded(q, tOld, q, t1 + "_" + t3 + t2, "0-10-1",
							tapes);
				}

				// t1 != 0 and t2 != 0: set marker to t4, multiply ...
				// --> exit
				else {
					t4 = "0";
					// multiply
					int qNew = getNextQ(iT1, iT2, 0, 0);
					// exit to q0-q(radix-1)
					qNew = qNew + q - radix;

					int newVal = multiply(iT1, iT2, 0, 0);
					t3 = Integer.toString(newVal % radix, radix);

					tc.addRulePadded(q, tOld, qNew, t1 + t2 + t3 + m,
							"0-1-1-1", tapes);
				}
			}
		}

		return q + 1;
	}

	// correct positions to marker
	protected int addRule_CorrectPositionToM(int q, int exitNormal,
			int[] tapes, int marker) {

		String m = Integer.toString(marker, radix);
		// iT1 to radix, meaning "_"
		for (int iT1 = 0; iT1 <= radix; iT1++) {
			// iT2 to radix, meaning "_"
			for (int iT2 = 0; iT2 <= radix; iT2++) {
				// iT3 to radix, meaning "_"
				for (int iT3 = 0; iT3 <= radix; iT3++) {
					// iT4 either -1 or 0
					for (int iT4 = -1; iT4 < 1; iT4++) {
						String t1 = (iT1 == radix) ? "_" : Integer.toString(
								iT1, radix);
						String t2 = (iT2 == radix) ? "_" : Integer.toString(
								iT2, radix);
						String t3 = (iT3 == radix) ? "_" : Integer.toString(
								iT3, radix);
						// 0 means marker, -1 means "_" !!
						String t4 = (iT4 == -1) ? "_" : m;

						String tOld = new String(t1 + t2 + t3 + t4);
						String tNew = new String(tOld);

						// t1 != "_" continue multiplication
						if (iT1 != radix) {
							if (iT2 != radix) {
								// XXX_ --> continue
								if (t4.compareTo(m) != 0) {
									// 0X__ --> shortcut, continue
									if (iT1 == 0) {
										String t3New;
										if(iT3 == radix){
											t3New = "0";
										} else{
											t3New = t3;
										}										
										tNew = t1 + t2 + t3New + t4;
										tc.addRulePadded(q, tOld, q, tNew,
												"-10-10", tapes);
									} else {
										tc.addRulePadded(q, tOld, q, tNew,
												"0111", tapes);
									}
								}
								// XXX0, t2>0 --> exit
								// else if (t4.compareTo(m) == 0 && iT2 > 0 &&
								// iT3 != radix) {
								else if (t4.compareTo(m) == 0 && iT2 > 0) {

									// multiply
									int qNew = getNextQ(iT1, iT2, iT3, 0);
									// exit to q0-q(radix-1)
									qNew = qNew + q - radix - 1;

									tNew = getMultipliedValues(iT1, iT2, iT3,
											t4, 0);
									// marker found, multiply --> exit
									tc.addRulePadded(q, tOld, qNew, tNew,
											"0-1-1-1", tapes);
								}

							}
						}
						// t1 == "_" cleanup: overwrite t2 with t3
						else {
							// no marker
							if (t4.compareTo(m) != 0) {
								if (iT2 != radix) {
									if (iT3 == radix) {
										// _X__ --> continue
										tc.addRulePadded(q, tOld, q, tNew,
												"0010", tapes);
									} else {
										// _XX_ overwrite --> continue
										t2 = t3;
										t3 = "_";
										tNew = t1 + t2 + t3 + t4;
										tc.addRulePadded(q, tOld, q, tNew,
												"0111", tapes);
									}
								}
							}
							// on marker
							else {
								// marker found, continue overwrite
								if (iT2 == radix && iT3 != radix) {
									// _XX0 or __X0
									// _XX_ overwrite --> continue
									t2 = t3;
									t3 = "_";
									tNew = t1 + t2 + t3 + t4;
									tc.addRulePadded(q, tOld, q, tNew, "0110",
											tapes);

								}// marker found, continue overwrite
								else if (iT2 != radix && iT3 == radix) {
									// _X_0 --> continue
									tc.addRulePadded(q, tOld, q, tNew, "0010",
											tapes);
								}
								// done, delete marker --> exit
								else if (tOld.compareTo("___" + m) == 0) {
									addRulePart_EndMultiplication(q,
											exitNormal, tapes, marker);
								} else {
									// _XX0 --> overwrite, continue
									t2 = t3;
									t3 = "_";
									tNew = new String(t1 + t2 + t3 + t4);
									tc.addRulePadded(q, tOld, q, tNew, "0110",
											tapes);
								}
							}
						}

					}

				}
			}
		}
		return q + 1;

	}

	protected void addRulePart_EndMultiplication(int q, int exitNormal,
			int[] tapes, int marker) {
		String m = Integer.toString(marker, radix);
		tc.addRulePadded(q, "___" + m, exitNormal, "____", "0001", tapes);
	}

	// move trailing "0" from t4 to t2
	protected int addRule_MoveTrailingZeros(int q, int exitNormal, int[] tapes,
			int marker) {
		// t1, t2, t3 blank, t4 filled with "0"
		tc.addRulePadded(q, "___0", q, "_0__", "0101", tapes);
		// end --> exit
		tc.addRulePadded(q, "____", exitNormal, "____", "0-10-1", tapes);
		return q + 1;
	}

	protected int getNextQ(int iT1, int iT2, int iT3, int q) {
		// new value
		int newVal = multiply(iT1, iT2, iT3, q);
		return (newVal / radix); // integer truncation
	}

	protected String getMultipliedValues(int iT1, int iT2, int iT3, String t4,
			int q) {

		// new value
		int newVal = multiply(iT1, iT2, iT3, q);
		// tapes
		String t1 = Integer.toString(iT1, radix);
		String t2 = (iT2 == radix) ? "_" : Integer.toString(iT2, radix);
		String t3 = Integer.toString(newVal % radix, radix);
		// avoid leading zero
		if (q == 0 && iT2 == radix && iT3 == radix) {
			t3 = "_";
		}
		return t1 + t2 + t3 + t4;
	}

	protected int multiply(int iT1, int iT2, int iT3, int q) {
		iT2 = (iT2 == radix) ? 0 : iT2;
		iT3 = (iT3 == radix) ? 0 : iT3;
		return ((iT1 * iT2) + iT3 + q);
	}
}
